{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "6a44f010",
      "metadata": {
        "id": "6a44f010"
      },
      "source": [
        "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langchain-ai/langchain-academy/blob/main/module-1/agent.ipynb) [![Open in LangChain Academy](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66e9eba12c7b7688aa3dbb5e_LCA-badge-green.svg)](https://academy.langchain.com/courses/take/intro-to-langgraph/lessons/58239232-lesson-6-agent)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "98f5e36a-da49-4ae2-8c74-b910a2f992fc",
      "metadata": {
        "id": "98f5e36a-da49-4ae2-8c74-b910a2f992fc"
      },
      "source": [
        "# Agent\n",
        "\n",
        "## Review\n",
        "\n",
        "We built a router.\n",
        "\n",
        "* Our chat model will decide to make a tool call or not based upon the user input\n",
        "* We use a conditional edge to route to a node that will call our tool or simply end\n",
        "\n",
        "![Screenshot 2024-08-21 at 12.44.33 PM.png](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66dbac0ba0bd34b541c448cc_agent1.png)\n",
        "\n",
        "## Goals\n",
        "\n",
        "Now, we can extend this into a generic agent architecture.\n",
        "\n",
        "In the above router, we invoked the model and, if it chose to call a tool, we returned a `ToolMessage` to the user.\n",
        "\n",
        "But, what if we simply pass that `ToolMessage` *back to the model*?\n",
        "\n",
        "We can let it either (1) call another tool or (2) respond directly.\n",
        "\n",
        "This is the intuition behind [ReAct](https://react-lm.github.io/), a general agent architecture.\n",
        "  \n",
        "* `act` - let the model call specific tools\n",
        "* `observe` - pass the tool output back to the model\n",
        "* `reason` - let the model reason about the tool output to decide what to do next (e.g., call another tool or just respond directly)\n",
        "\n",
        "This [general purpose architecture](https://blog.langchain.dev/planning-for-agents/) can be applied to many types of tools.\n",
        "\n",
        "![Screenshot 2024-08-21 at 12.45.43 PM.png](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66dbac0b4a2c1e5e02f3e78b_agent2.png)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "63edff5a-724b-474d-9db8-37f0ae936c76",
      "metadata": {
        "id": "63edff5a-724b-474d-9db8-37f0ae936c76"
      },
      "outputs": [],
      "source": [
        "%%capture --no-stderr\n",
        "%pip install --quiet -U langchain_google_genai langchain_core langgraph"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "356a6482",
      "metadata": {
        "id": "356a6482"
      },
      "outputs": [],
      "source": [
        "import os, getpass\n",
        "\n",
        "def _set_env(var: str):\n",
        "    if not os.environ.get(var):\n",
        "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
        "\n",
        "_set_env(\"GEMINI_API_KEY\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "dba35a12",
      "metadata": {
        "id": "dba35a12"
      },
      "source": [
        "Here, we'll use [LangSmith](https://docs.smith.langchain.com/) for [tracing](https://docs.smith.langchain.com/concepts/tracing).\n",
        "\n",
        "We'll log to a project, `langchain-academy`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "60e6f1eb",
      "metadata": {
        "id": "60e6f1eb"
      },
      "outputs": [],
      "source": [
        "_set_env(\"LANGCHAIN_API_KEY\")\n",
        "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
        "os.environ[\"LANGCHAIN_PROJECT\"] = \"langchain-academy\""
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bhk7X00flFbe",
      "metadata": {
        "id": "bhk7X00flFbe"
      },
      "source": [
        "**Note:** Obtain the necessary json key from the Google Cloud Console by following the instructions outlined in step 21_langchain_ecosystem/langchain/-01_gemini_standalone/Gemini_API_python.ipynb file. Once acquired, load the json key in Google Colab to proceed with the project."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "CDHfjEHmkg26",
      "metadata": {
        "id": "CDHfjEHmkg26"
      },
      "outputs": [],
      "source": [
        "import os\n",
        "os.environ[\"GOOGLE_APPLICATION_CREDENTIALS\"] = \"/content/<your-json-key-here>\""
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "71795ff1-d6a7-448d-8b55-88bbd1ed3dbe",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "collapsed": true,
        "id": "71795ff1-d6a7-448d-8b55-88bbd1ed3dbe",
        "outputId": "2cbdef25-4281-49b1-de59-28244a24eba6"
      },
      "outputs": [
        {
          "name": "stderr",
          "output_type": "stream",
          "text": [
            "/Users/mjs/Documents/code/panaversity-official/learn-applied-generative-ai-fundamentals/.conda/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
            "  from .autonotebook import tqdm as notebook_tqdm\n",
            "Key 'title' is not supported in schema, ignoring\n",
            "Key 'title' is not supported in schema, ignoring\n",
            "Key 'title' is not supported in schema, ignoring\n",
            "Key 'title' is not supported in schema, ignoring\n",
            "Key 'title' is not supported in schema, ignoring\n",
            "Key 'title' is not supported in schema, ignoring\n",
            "Key 'title' is not supported in schema, ignoring\n",
            "Key 'title' is not supported in schema, ignoring\n",
            "Key 'title' is not supported in schema, ignoring\n"
          ]
        }
      ],
      "source": [
        "from langchain_google_genai import ChatGoogleGenerativeAI\n",
        "\n",
        "def multiply(a: int, b: int) -> int:\n",
        "    \"\"\"Multiply a and b.\n",
        "\n",
        "    Args:\n",
        "        a: first int\n",
        "        b: second int\n",
        "    \"\"\"\n",
        "    return a * b\n",
        "\n",
        "# This will be a tool\n",
        "def add(a: int, b: int) -> int:\n",
        "    \"\"\"Adds a and b.\n",
        "\n",
        "    Args:\n",
        "        a: first int\n",
        "        b: second int\n",
        "    \"\"\"\n",
        "    return a + b\n",
        "\n",
        "def divide(a: int, b: int) -> float:\n",
        "    \"\"\"Divide a and b.\n",
        "\n",
        "    Args:\n",
        "        a: first int\n",
        "        b: second int\n",
        "    \"\"\"\n",
        "    return a / b\n",
        "\n",
        "tools = [add, multiply, divide]\n",
        "llm = ChatGoogleGenerativeAI(model=\"gemini-1.5-flash\", api_key =  os.environ[\"GEMINI_API_KEY\"])\n",
        "llm_with_tools = llm.bind_tools(tools)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a2cec014-3023-405c-be79-de8fc7adb346",
      "metadata": {
        "id": "a2cec014-3023-405c-be79-de8fc7adb346"
      },
      "source": [
        "Let's create our LLM and prompt it with the overall desired agent behavior."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "d061813f-ebc0-432c-91ec-3b42b15c30b6",
      "metadata": {
        "id": "d061813f-ebc0-432c-91ec-3b42b15c30b6"
      },
      "outputs": [],
      "source": [
        "from langgraph.graph import MessagesState\n",
        "from langchain_core.messages import HumanMessage, SystemMessage\n",
        "\n",
        "# System message\n",
        "sys_msg = SystemMessage(content=\"You are a helpful assistant tasked with performing arithmetic on a set of inputs.\")\n",
        "\n",
        "# Node\n",
        "def assistant(state: MessagesState) -> MessagesState:\n",
        "   return {\"messages\": [llm_with_tools.invoke([sys_msg] + state[\"messages\"])]}"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4eb43343-9a6f-42cb-86e6-4380f928633c",
      "metadata": {
        "id": "4eb43343-9a6f-42cb-86e6-4380f928633c"
      },
      "source": [
        "As before, we use `MessagesState` and define a `Tools` node with our list of tools.\n",
        "\n",
        "The `Assistant` node is just our model with bound tools.\n",
        "\n",
        "We create a graph with `Assistant` and `Tools` nodes.\n",
        "\n",
        "We add `tools_condition` edge, which routes to `End` or to `Tools` based on  whether the `Assistant` calls a tool.\n",
        "\n",
        "Now, we add one new step:\n",
        "\n",
        "We connect the `Tools` node *back* to the `Assistant`, forming a loop.\n",
        "\n",
        "* After the `assistant` node executes, `tools_condition` checks if the model's output is a tool call.\n",
        "* If it is a tool call, the flow is directed to the `tools` node.\n",
        "* The `tools` node connects back to `assistant`.\n",
        "* This loop continues as long as the model decides to call tools.\n",
        "* If the model response is not a tool call, the flow is directed to END, terminating the process."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "aef13cd4-05a6-4084-a620-2e7b91d9a72f",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 266
        },
        "id": "aef13cd4-05a6-4084-a620-2e7b91d9a72f",
        "outputId": "73bbc572-0cbf-4955-b518-d006f3afc7a8"
      },
      "outputs": [
        {
          "data": {
            "image/jpeg": "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",
            "text/plain": [
              "<IPython.core.display.Image object>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "from langgraph.graph import START, StateGraph\n",
        "from langgraph.prebuilt import tools_condition\n",
        "from langgraph.prebuilt import ToolNode\n",
        "from IPython.display import Image, display\n",
        "from langgraph.graph.state import CompiledStateGraph\n",
        "\n",
        "# Graph\n",
        "builder: StateGraph = StateGraph(MessagesState)\n",
        "\n",
        "# Define nodes: these do the work\n",
        "builder.add_node(\"assistant\", assistant)\n",
        "builder.add_node(\"tools\", ToolNode(tools))\n",
        "\n",
        "# Define edges: these determine how the control flow moves\n",
        "builder.add_edge(START, \"assistant\")\n",
        "builder.add_conditional_edges(\n",
        "    \"assistant\",\n",
        "    # If the latest message (result) from assistant is a tool call -> tools_condition routes to tools\n",
        "    # If the latest message (result) from assistant is a not a tool call -> tools_condition routes to END\n",
        "    tools_condition,\n",
        ")\n",
        "builder.add_edge(\"tools\", \"assistant\")\n",
        "react_graph: CompiledStateGraph = builder.compile()\n",
        "\n",
        "# Show\n",
        "display(Image(react_graph.get_graph(xray=True).draw_mermaid_png()))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "75602459-d8ca-47b4-9518-3f38343ebfe4",
      "metadata": {
        "id": "75602459-d8ca-47b4-9518-3f38343ebfe4"
      },
      "outputs": [],
      "source": [
        "messages = [HumanMessage(content=\"Add 3 and 4. Multiply the output by 2. Divide the output by 5\")]\n",
        "messages = react_graph.invoke({\"messages\": messages})"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "b517142d-c40c-48bf-a5b8-c8409427aa79",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "b517142d-c40c-48bf-a5b8-c8409427aa79",
        "outputId": "1ee526f1-8799-4ca6-bc31-ee4d421fe991"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "================================\u001b[1m Human Message \u001b[0m=================================\n",
            "\n",
            "Add 3 and 4. Multiply the output by 2. Divide the output by 5\n",
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "\n",
            "['\\n', '\\n', '\\nThe answer is 2.8. \\n']\n",
            "Tool Calls:\n",
            "  divide (83b36a5a-df4d-438c-a3af-bbffa544fa31)\n",
            " Call ID: 83b36a5a-df4d-438c-a3af-bbffa544fa31\n",
            "  Args:\n",
            "    a: None\n",
            "    b: 5.0\n",
            "  add (44dc85d6-15a0-4136-9a29-a2b845ac53a3)\n",
            " Call ID: 44dc85d6-15a0-4136-9a29-a2b845ac53a3\n",
            "  Args:\n",
            "    a: 3.0\n",
            "    b: 4.0\n",
            "  multiply (d76f059e-08ac-4322-800b-64b310dd4209)\n",
            " Call ID: d76f059e-08ac-4322-800b-64b310dd4209\n",
            "  Args:\n",
            "    a: 7.0\n",
            "    b: 2.0\n",
            "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
            "Name: divide\n",
            "\n",
            "Error: 1 validation error for divide\n",
            "a\n",
            "  Input should be a valid integer [type=int_type, input_value=None, input_type=NoneType]\n",
            "    For further information visit https://errors.pydantic.dev/2.9/v/int_type\n",
            " Please fix your mistakes.\n",
            "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
            "Name: add\n",
            "\n",
            "7\n",
            "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
            "Name: multiply\n",
            "\n",
            "14\n",
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "\n",
            "The final result is 14.\n"
          ]
        }
      ],
      "source": [
        "for m in messages['messages']:\n",
        "    m.pretty_print()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "G6dPi8LytFp8",
      "metadata": {
        "id": "G6dPi8LytFp8"
      },
      "source": [
        "**Note:** In above error the problem is that multiple chained operations (\"add\", \"multiply\", and \"divide\") are being interpreted incorrectly, likely leading to premature calls to tools without resolving earlier steps as model is following <b>DMAS</b> rule here. So we will improved the prompt and try again"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "-WyCc3llrmJz",
      "metadata": {
        "id": "-WyCc3llrmJz"
      },
      "outputs": [],
      "source": [
        "messages = [HumanMessage(content=\"First Add 3 and 4 then multiply answer with 2 and then divide answer with 5. Dont perform division initially\")]\n",
        "messages = react_graph.invoke({\"messages\": messages})"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "_JLB3nGQr5cu",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "_JLB3nGQr5cu",
        "outputId": "f14745d0-4384-4604-8ed6-66cd42c713d9"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "================================\u001b[1m Human Message \u001b[0m=================================\n",
            "\n",
            "First Add 3 and 4 then multiply answer with 2 and then divide answer with 5. Dont perform division initially\n",
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "Tool Calls:\n",
            "  add (463e859b-1a3c-49f0-be2d-333096281fa5)\n",
            " Call ID: 463e859b-1a3c-49f0-be2d-333096281fa5\n",
            "  Args:\n",
            "    a: 3.0\n",
            "    b: 4.0\n",
            "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
            "Name: add\n",
            "\n",
            "7\n",
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "Tool Calls:\n",
            "  multiply (a22bf5df-4161-4925-872b-ed5bbddbfd10)\n",
            " Call ID: a22bf5df-4161-4925-872b-ed5bbddbfd10\n",
            "  Args:\n",
            "    a: 7.0\n",
            "    b: 2.0\n",
            "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
            "Name: multiply\n",
            "\n",
            "14\n",
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "Tool Calls:\n",
            "  divide (c4c3e229-16c0-4292-87da-a27b9fa40cea)\n",
            " Call ID: c4c3e229-16c0-4292-87da-a27b9fa40cea\n",
            "  Args:\n",
            "    a: 14.0\n",
            "    b: 5.0\n",
            "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
            "Name: divide\n",
            "\n",
            "2.8\n",
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "\n",
            "The final answer is 2.8.\n"
          ]
        }
      ],
      "source": [
        "for m in messages['messages']:\n",
        "    m.pretty_print()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ad869f22-9bfb-4cbe-9f30-8a307c5cdda2",
      "metadata": {
        "id": "ad869f22-9bfb-4cbe-9f30-8a307c5cdda2"
      },
      "source": [
        "## LangSmith\n",
        "\n",
        "We can look at traces in LangSmith."
      ]
    }
  ],
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "display_name": "Python 3 (ipykernel)",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.12.6"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}
